/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use crate::commands::credential_commands;
use crate::common::constants::*;
use crate::common::types::*;
use crate::traits::user_db_manager::{UserDbManagerRegistry, User, CredentialFilter, Credential};
use crate::{log_e, singleton_registry, Box, Vec, CString};
use crate::traits::auth_policy_manager::{AuthPolicyManagerRegistry};
use crate::traits::time_keeper::{TimeKeeperRegistry};
use crate::traits::event_manager::{EventManagerRegistry, Event, EventType};

fn check_mspc_redundancy_user_info() -> () {
    let mock_user_ids: Vec<i32> = Vec::new();
    for user_id in mock_user_ids {
        if let Err(ErrorCode::NotEnrolled) =  UserDbManagerRegistry::get().get_pin_credential(user_id) {
            log_e!("mspc redundancy userId:{}", user_id);
            EventManagerRegistry::get_mut().record_event(&Event::default());
        }
    }
}

fn check_mspc_redundancy_cred_info(user_id: i32, mspc_cred_infos: &Vec<Credential>) {
    for mspc_cred in mspc_cred_infos.clone() {
        let credential_filter: CredentialFilter = Box::new(move |cred: &Credential| {
            cred.user_id == user_id && cred.credential_id == mspc_cred.credential_id
        });
        if let Err(ErrorCode::NotEnrolled) =  UserDbManagerRegistry::get().get_credential_list(credential_filter) {
            log_e!("mspc redundancy authType:{}", mspc_cred.auth_type as i32);
            EventManagerRegistry::get_mut().record_event(&Event::default());
        }
    }
}

fn get_mspc_user_cred_info_wrapper(_user_id: i32) -> Result<(bool, Option<u64>, Vec<Credential>), ErrorCode> {
    let is_mspc_available: bool = true;
    let mspc_cred_infos: Vec<Credential> = Vec::new();
    let sec_uid: Option<u64> = if mspc_cred_infos.len() != 0 { Some(0) } else { None };
    Ok((is_mspc_available, sec_uid, mspc_cred_infos))
}

fn check_tee_user_info_valid(user_id: i32, is_mspc_available: bool, sec_uid:Option<u64>) -> Result<bool, ErrorCode> {
    if !UserDbManagerRegistry::get().check_user_valid(user_id)? {
        log_e!("tee invalid userId:{}", user_id as i32);
        EventManagerRegistry::get_mut().record_event(&Event::default());
        UserDbManagerRegistry::get_mut().remove_user(user_id)?;
        return Ok(false);
    }

    if !is_mspc_available {
        return Ok(true);
    }

    let sec_uid = match sec_uid {
        Some(value) => value,
        None => {
            return Ok(true);
        }
    };

    let user_info: User = UserDbManagerRegistry::get().get_user(user_id)?;
    if user_info.sec_uid != sec_uid {
        log_e!("mspc add tee mismatch, secUid:{},{}", sec_uid, user_info.sec_uid);
        EventManagerRegistry::get_mut().record_event(&Event::default());
        return Ok(false);
    }
    return Ok(true);
}

fn check_private_pin_valid(user_id: i32, cred_info: &Credential) -> bool {
    if cred_info.auth_type != AuthType::PrivatePin {
        return true;
    }

    let credential_filter: CredentialFilter = Box::new(move |cred: &Credential| {
        cred.user_id == user_id && cred.auth_type == AuthType::PrivatePin
    });

    let credentials = match UserDbManagerRegistry::get().get_credential_list(credential_filter) {
        Ok(value) => value,
        Err(err) => {
            log_e!("cann't find privatePin, ret:{}", err as i32);
            return false;
        }
    };

    let has_pin_question = credentials.iter().any(|cred| cred.credential_type == CredentialType::PinQuestion);
    let has_non_pin_question = credentials.iter().any(|cred| cred.credential_type != CredentialType::PinQuestion);
    if has_pin_question && has_non_pin_question {
        return true;
    } else {
        log_e!("privatePin credType:{} check fail, without other credType", cred_info.credential_type as i32);
        return false;
    }
}

fn check_abandone_pin_expired(cred_info: &Credential) -> Result<bool, ErrorCode> {
    if cred_info.auth_type != AuthType::Pin {
        return Ok(true);
    }

    if !cred_info.abandon_flag {
        log_e!("cred in abandone");
        return Ok(false);
    }

    //todo IsAdvSecMode

    let current_time = match TimeKeeperRegistry::get().get_rtc_time() {
        Ok(value) => value,
        Err(err) => {
            log_e!("get_rtc_time fail, err:{}", err as i32);
            return Err(ErrorCode::GeneralError);
        }
    };

    if current_time < cred_info.abandon_sys_time {
        log_e!("bad time, current_time:{}, abandon_sys_time:{}", current_time, cred_info.abandon_sys_time);
        return Err(ErrorCode::GeneralError);
    }

    if current_time - cred_info.abandon_sys_time >= ABANDON_PIN_VALID_PERIOD {
        log_e!("cred abandoned, current_time:{}, abandon_sys_time:{}", current_time, cred_info.abandon_sys_time);
        return Ok(false);
    }
    return Ok(true);
}

fn check_cred_depend_pin_valid(user_id: i32, auth_type: AuthType) -> Result<bool, ErrorCode> {
    let auth_policy = AuthPolicyManagerRegistry::get().get_auth_policy(auth_type)?;
    if !auth_policy.depends_on_pin() {
        return Ok(true);
    }

    if let Err(e) =  UserDbManagerRegistry::get().get_pin_credential(user_id) {
        log_e!("query pin fail ret:{}", e as i32);
        if e == ErrorCode::NotEnrolled {
            return Ok(false);
        }
        return Err(e);
    }
    Ok(true)
}

fn check_tee_cred_info_valid(user_id:i32 ,cred_info: &Credential) -> Result<bool, ErrorCode> {
    let check_result = check_cred_depend_pin_valid(user_id, cred_info.auth_type)?;
    if !check_result {
        log_e!("check authType:{} failed, whithout pin", cred_info.auth_type as i32);
        EventManagerRegistry::get_mut().record_event(&Event::default());
        UserDbManagerRegistry::get_mut().remove_credential(cred_info.credential_id)?;
        return Ok(false);
    }

    let user_info = UserDbManagerRegistry::get().get_user(user_id)?;

    let is_valid: bool = match cred_info.auth_type {
        AuthType::Default => false,
        AuthType::Pin => check_abandone_pin_expired(cred_info)?,
        AuthType::PrivatePin => check_private_pin_valid(user_id, cred_info),
        AuthType::Face => user_info.user_type != UserType::Private,
        _ => true,
    };
    if !is_valid {
        log_e!("check authType:{} failed", cred_info.auth_type as i32);
        EventManagerRegistry::get_mut().record_event(&Event::default());
        UserDbManagerRegistry::get_mut().remove_credential(cred_info.credential_id)?;
        return Ok(false);
    }
    return Ok(true);
}

fn check_tee_cred_with_mspc(user_id: i32, cred_indo: &Credential, mspc_cred_infos: &Vec<Credential>) -> Result<bool, ErrorCode> {
    match mspc_cred_infos.iter().find(|value| value.credential_id == cred_indo.credential_id) {
        None => {
            if cred_indo.storage_type != CredStorageType::StorageTypeMspc {
                return Ok(true);
            }
            EventManagerRegistry::get_mut().record_event(&Event::default());
            UserDbManagerRegistry::get_mut().remove_credential(cred_indo.credential_id)?;
            return Ok(false);
        },
        Some(value) => {
            if value.auth_type == cred_indo.auth_type && value.template_id == cred_indo.template_id &&
                cred_indo.storage_type == CredStorageType::StorageTypeMspc {
                return Ok(true);
            }
            EventManagerRegistry::get_mut().record_event(&Event::default());
            let mut cred_info_temp = cred_indo.clone();
            cred_info_temp.storage_type = CredStorageType::StorageTypeMspc;
            cred_info_temp.auth_type = value.auth_type;
            cred_info_temp.template_id = value.template_id;
            //todo
            UserDbManagerRegistry::get_mut().remove_credential(cred_indo.credential_id)?;
            UserDbManagerRegistry::get_mut().add_credential(user_id, &cred_info_temp)?;
            return Ok(false);
        }
    }
}

fn check_tee_cred_infos_valid(user_id:i32, is_mspc_available: bool, mspc_cred_infos: &Vec<Credential>) -> Result<bool, ErrorCode> {
    let credential_filter: CredentialFilter = Box::new(move |cred: &Credential| {cred.user_id == user_id});
    let user_cred_infos: Vec<Credential> = UserDbManagerRegistry::get().get_credential_list(credential_filter)?;

    let mut is_valid = true;
    for cred_info in user_cred_infos {
        if !check_tee_cred_info_valid(user_id, &cred_info)? {
            is_valid = false;
            continue;
        }
        if is_mspc_available && !check_tee_cred_with_mspc(user_id, &cred_info, mspc_cred_infos)? {
            is_valid = false;
        }
    }

    Ok(is_valid)
}

pub fn clear_user_db_redundancy_info() -> Result<(), ErrorCode> {
    check_mspc_redundancy_user_info();
    
    let user_ids = UserDbManagerRegistry::get().get_all_user_id()?;
    let mut is_user_valid = true;
    let mut is_cred_valid = true;
    for user_id in user_ids {

        let (is_mspc_available, sec_uid, mspc_cred_infos) = get_mspc_user_cred_info_wrapper(user_id)?;
        check_mspc_redundancy_cred_info(user_id, &mspc_cred_infos);
    
        match check_tee_user_info_valid(user_id, is_mspc_available, sec_uid) {
            Err(err) => {
                log_e!("check_tee_user_info_valid fail:{}", err as i32);
                UserDbManagerRegistry::get_mut().read_db()?;
                return Ok(());
            },
            Ok(value) => { is_user_valid = value; }
        }
        match check_tee_cred_infos_valid(user_id, is_mspc_available, &mspc_cred_infos) {
            Err(err) => {
                log_e!("check_tee_cred_infos_valid fail:{}", err as i32);
                UserDbManagerRegistry::get_mut().read_db()?;
                return Ok(());
            },
            Ok(value) => { is_cred_valid = value; }
        }
    }
    if !is_user_valid || !is_cred_valid {
        if let Err(err) = UserDbManagerRegistry::get_mut().write_db() {
            log_e!("update db fail:{}", err as i32);
        }
    }

    Ok(())
}